/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2012  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.ui.controlpanel;

import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;
import java.util.EventObject;

import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFormattedTextField;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.InFormat;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.layout.MinimizerBarnesHut;
import org.sosy_lab.ccvisu.layout.TwoPhaseMinimizer;
import org.sosy_lab.ccvisu.readers.ReaderData;
import org.sosy_lab.ccvisu.ui.StopTaskButton;
import org.sosy_lab.util.interfaces.WorkerManager;

@SuppressWarnings("serial")
public class ToolPanelLayouter extends ControlPanel {

  private JCheckBox           weightedCheckbox            = new JCheckBox();
  private JFormattedTextField gravitationTextField        = new JFormattedTextField(DecimalFormat.getInstance());

  private JComboBox<String>   repulsionTypeTextField      = new JComboBox<>();

  private JFormattedTextField repulsionExponentTextField  = new JFormattedTextField();
  private JCheckBox           animationCheckBox           = new JCheckBox();
  private JFormattedTextField attractionExponentTextField = new JFormattedTextField();
  private JSpinner            numOfIterationsSpinner      = new JSpinner();
  private JCheckBox           autoStopIteratingCheckBox   = new JCheckBox("Yes");

  private JSpinner            dimensionsSpinner           = new JSpinner();
  private JFormattedTextField scaleTextField              = new JFormattedTextField();
  private JButton             chooseInitialLayoutButton   = new JButton();
  private JTextField          initialLayoutTextField      = new JTextField();
  private JButton             startMinimizerButton        = new JButton("Run layouter");
  private JButton             randomLayoutButton          = new JButton("Random layout");
  private StopTaskButton      stopTaskButton;

  private final Options options;
  private final WorkerManager workerManager;

  public ToolPanelLayouter(Options options, WorkerManager workerManager) {
    this.options = options;
    this.workerManager = workerManager;

    initComponents();
  }

  @Override
  public void applyOptions() {
    options.getOption(OptionsEnum.dim).set(((Integer) dimensionsSpinner.getValue()).intValue());
    options.getOption(OptionsEnum.iter).set(Integer.parseInt(this.numOfIterationsSpinner.getValue().toString()));
    options.getOption(OptionsEnum.autoStopIterating).set(autoStopIteratingCheckBox.isSelected());
    options.getOption(OptionsEnum.initLayout).set(initialLayoutTextField.getText());
    options.attrExponent = (Float) attractionExponentTextField.getValue();
    options.repuExponent = (Float) repulsionExponentTextField.getValue();
    options.gravitation = ((Number) gravitationTextField.getValue()).floatValue();
    options.vertRepu = (repulsionTypeTextField.getSelectedIndex() == 1);
    options.noWeight = !weightedCheckbox.isSelected();
    options.getOption(OptionsEnum.scalePos).set((Float) scaleTextField.getValue());
    options.getOption(OptionsEnum.anim).set(animationCheckBox.isSelected());
  }

  @Override
  public void loadOptions() {
    scaleTextField.setValue(new Float(options.getOption(OptionsEnum.scalePos).getFloat()));
    dimensionsSpinner.setValue(options.getOption(OptionsEnum.dim).getInt());
    numOfIterationsSpinner.setValue(options.getOption(OptionsEnum.iter).getInt());
    autoStopIteratingCheckBox.setSelected(options.getOption(OptionsEnum.autoStopIterating).getBool());
    initialLayoutTextField.setText(options.getOption(OptionsEnum.initLayout).getString());
    attractionExponentTextField.setValue(new Float(options.attrExponent));
    gravitationTextField.setValue(new Float(options.gravitation));
    repulsionExponentTextField.setValue(new Float(options.repuExponent));
    weightedCheckbox.setSelected(!options.noWeight);
    animationCheckBox.setSelected(options.getOption(OptionsEnum.anim).getBool());

    if (options.vertRepu) {
      repulsionTypeTextField.setSelectedIndex(1);
    } else {
      repulsionTypeTextField.setSelectedIndex(0);
    }
  }

  public void initComponents() {
    stopTaskButton = new StopTaskButton(workerManager);

    JPanel initLayoutPanel = new JPanel(new BorderLayout());
    initLayoutPanel.add(initialLayoutTextField, BorderLayout.CENTER);
    initLayoutPanel.add(chooseInitialLayoutButton, BorderLayout.EAST);

    JPanel controlPanel = new JPanel();
    controlPanel.add(randomLayoutButton);
    controlPanel.add(startMinimizerButton);
    controlPanel.add(stopTaskButton);

    JPanel paramsPanel = new JPanel();
    paramsPanel.setLayout(new GridBagLayout());
    addOptionControls(paramsPanel, "Weighted edges?", weightedCheckbox);
    addOptionControls(paramsPanel, "Animation during layouting?", animationCheckBox);
    addOptionControls(paramsPanel, "Attraction exponent:", attractionExponentTextField);
    addOptionControls(paramsPanel, "Repulsion exponent:", repulsionExponentTextField);
    addOptionControls(paramsPanel, "Repulsion type:", repulsionTypeTextField);
    addOptionControls(paramsPanel, "Gravitation:", gravitationTextField);
    addOptionControls(paramsPanel, "Initial layout:", initLayoutPanel);
    addOptionControls(paramsPanel, "Dimensions:", dimensionsSpinner);
    addOptionControls(paramsPanel, "Scale factor:", scaleTextField);
    addOptionControls(paramsPanel, "Number of iterations:", numOfIterationsSpinner);
    addOptionControls(paramsPanel, "Stop iterating on energy minimum?", autoStopIteratingCheckBox);

    this.setLayout(new BorderLayout());
    this.add(paramsPanel, BorderLayout.CENTER);
    this.add(controlPanel, BorderLayout.SOUTH);

    animationCheckBox.setText("Yes");
    repulsionTypeTextField.setModel(
        new DefaultComboBoxModel<String>(new String[] { "Edge repulsion", "Vertex repulsion" }));
    chooseInitialLayoutButton.setText("Choose...");
    weightedCheckbox.setText("Yes");

    setActionOnEnter(attractionExponentTextField);
    setActionOnEnter(repulsionExponentTextField);
    setActionOnEnter(gravitationTextField);
    setActionOnEnter(scaleTextField);
    setActionOnEnter(initialLayoutTextField);
    setActionOnEnter(dimensionsSpinner);
    setActionOnEnter(numOfIterationsSpinner);

    setMnemonics();
    registerListeners();
  }

  private void registerListeners() {
    startMinimizerButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        applyAndMinimize();
      }
    });

    randomLayoutButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent pE) {
        options.graph.setNodesToRandomPositions(options.graph.getVertices(), options.getOption(OptionsEnum.dim).getInt());
        options.graph.notifyAboutLayoutChange(new EventObject(this));
      }
    });

    chooseInitialLayoutButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent evt) {
        JFileChooser fileDialog = new JFileChooser(".");
        fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(InFormat.LAY));
        int outcome = fileDialog.showOpenDialog(null);
        if (outcome == JFileChooser.APPROVE_OPTION) {
          assert (fileDialog.getCurrentDirectory() != null);
          assert (fileDialog.getSelectedFile() != null);
          String fileName =
            fileDialog.getCurrentDirectory().toString() + File.separator
            + fileDialog.getSelectedFile().getName();
          initialLayoutTextField.setText(fileName);
        }
      }
    });
  }

  private void applyAndMinimize() {
    applyOptions();
    MinimizerBarnesHut minimizer = new TwoPhaseMinimizer(options);
    workerManager.addAndRunTask(minimizer, "Minimizer");
  }

  private void setActionOnEnter(JComponent component) {
    component.getInputMap().put(KeyStroke.getKeyStroke('\n'), "runMinimizer");
    component.getActionMap().put("runMinimizer", new AbstractAction() {
      @Override
      public void actionPerformed(ActionEvent e) {
        applyAndMinimize();
      }
    });
  }

  private void setMnemonics() {
    chooseInitialLayoutButton.setMnemonic('C');
    startMinimizerButton.setMnemonic('R');
    stopTaskButton.setMnemonic('S');
  }
}
